package humynn.engine
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Loader;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.net.URLLoader;
	import flash.net.URLRequest;

	/**
	 * Manages loading and providing access to images.
	 */
	public class ImageManager
	{
		public static var completed:Function;
		
		private static var _images:Array;
		
		private static var _urlLoader:URLLoader;
		private static var _imageLoader:Loader;
		
		private static var _loaderIndex:uint;
		
		public function ImageManager()
		{
		}
		
		/**
		 * Used to get the loading progress.
		 * 
		 * @returns The percent of the loaded images.
		 */
		public static function get progress():uint
		{
			return Math.round(_loaderIndex * 100 / _images.length); 
		}
		
		/**
		 * Starts preloading the image files in the images.txt file.
		 */
		public static function beginPreload():void
		{
			_images = new Array();
			
			_imageLoader = new Loader();
			_imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, imageLoaded);
			
			_urlLoader = new URLLoader();
			_urlLoader.addEventListener(Event.COMPLETE, infoLoaded);
			
			_loaderIndex = 0;
			
			_urlLoader.load(new URLRequest("data/images.txt"));
		}
		
		/**
		 * Called once the resource file ("images.txt") is loaded, and loads the
		 * image information from the file. It then begins the preloading cycle.
		 * 
		 * @param e Ignored.
		 */
		private static function infoLoaded(e:Event):void
		{
			var lines:Array = String(_urlLoader.data).split("\r\n");
			
			for each(var line:String in lines)
			{
				var data:Array = line.split(",");
				
				if(data.length >= 3)
				{
					var img:Image = new Image();
					img.fileName = data[0];
					img.cols = data[1];
					img.rows = data[2];
					
					for(var i:int = 3; i < data.length; i++)
					{
						if(data[i] == "flip")
						{
							i++;
							var flipFrames:Array = data[i].split(":");
							img.animations.push([uint(flipFrames[0]), uint(flipFrames[1]), "flip"]);
						}
						else if(data[i] == "rotate")
						{
							i++;
						}
						else
						{
							var anim:Array = String(data[i]).split(":");
							img.animations.push([uint(anim[0]), uint(anim[1])]);
						}
					}
					
					_images.push(img);
				}
			}
			
			loadNextImage();
		}
		
		/**
		 * Used to load the next image in the sequence if there is one.
		 */
		private static function loadNextImage():void
		{
			if(_loaderIndex >= _images.length)
			{
				if(completed != null)
				{
					completed();
				}
				return;
			}
			
			_imageLoader.load(new URLRequest("data/" + _images[_loaderIndex].fileName));
		}
		
		/**
		 * Once an image is loaded, this is called to store the image in memory
		 * for future use. Then it begins to load the next image in the list.
		 * Each animation frame is stored in its own BitmapData object.
		 */
		private static function imageLoaded(e:Event):void
		{
			var img:BitmapData = Bitmap(_imageLoader.content).bitmapData;
			var tileWidth:uint = Math.round(img.width / _images[_loaderIndex].cols);
			var tileHeight:uint = Math.round(img.height / _images[_loaderIndex].rows);
			var srcRect:Rectangle = new Rectangle(0, 0, tileWidth, tileHeight);
			
			for(var row:int = 0; row < _images[_loaderIndex].rows; row++)
			{
				for(var col:int = 0; col < _images[_loaderIndex].cols; col++)
				{
					var frame:BitmapData = new BitmapData(tileWidth, tileHeight, true, 0x00);
					
					srcRect.x = col * tileWidth;
					srcRect.y = row * tileHeight;
					
					frame.copyPixels(img, srcRect, new Point(), null, null, true);
					
					_images[_loaderIndex].frames.push(frame.clone());
				}
			}
			
			_loaderIndex++;
			loadNextImage();
		}
		
		/**
		 * Gets the bitmap data based on the given file name, animation index,
		 * and animation frame.
		 * 
		 * @param file The name of the file.
		 * @param animIndex The index of the animation.
		 * @param animFrame The frame in the current animation.
		 * 
		 * @returns The BitmapData of the given file name and animation data.
		 */
		public static function getBitmapData(file:String, animIndex:uint = 0, animFrame:uint = 0):BitmapData
		{
			var imgIndex:int = findBitmapIndex(file);
			
			if(imgIndex == -1)
			{
				trace("Can't find " + file);
				return null;
			}
			
			return _images[imgIndex].frames[_images[imgIndex].animations[animIndex][0] + animFrame];
		}
		
		/**
		 * Checks to see if the requested file exists, and if it does,
		 * it returns the index of that file.
		 * 
		 * @param file The file name to look up.
		 * 
		 * @returns The index of the file we found or -1 if it was not found.
		 */
		private static function findBitmapIndex(file:String):int
		{
			for(var i:int = 0; i < _images.length; i++)
			{
				if(_images[i].fileName == file)
				{
					return i;
				}
			}
			
			return -1;
		}
		
		/**
		 * Gets the number of frames in an animation.
		 * 
		 * @param file The name of the image file.
		 * @param animIndex The index to the animation.
		 * 
		 * @returns The number of frames in the animation or 0 if the animation was not found.
		 */
		public static function getFrameCount(file:String, animIndex:uint):uint
		{
			var i:int  = findBitmapIndex(file);
			
			if(i == -1)
			{
				trace("Can't find " + file);
				return 0;
			}
			
			if(animIndex > _images[i].animations.length)
			{
				trace("animIndex not in range");
				return 0;
			}
			
			var firstFrame:uint = _images[i].animations[animIndex][0];
			var lastFrame:uint = _images[i].animations[animIndex][1];
			
			return (lastFrame - firstFrame + 1);
		}
		
		/**
		 * Gets the width of the image.
		 * 
		 * @param file The name of the image file.
		 * 
		 * @returns The width of a single tile in the image.
		 */
		public static function getTileWidth(file:String):uint
		{
			var i:int  = findBitmapIndex(file);
			
			if(i == -1)
			{
				trace("Can't find " + file);
				return 0;
			}
			
			return _images[i].frames[0].width;
		}
		
		/**
		 * Gets the height of the image.
		 * 
		 * @param file The name of the image file.
		 * 
		 * @returns The height of a single tile in the image.
		 */
		public static function getTileHeight(file:String):uint
		{
			var i:int  = findBitmapIndex(file);
			
			if(i == -1)
			{
				trace("Can't find " + file);
				return 0;
			}
			
			return _images[i].frames[0].height;
		}
	}
}